function Cartesian3(x, y, z) {
    this.x = x || 0
    this.y = y || 0
    this.z = z || 0
}
let scratchN = new Cartesian3()
let scratchK = new Cartesian3()
const wgs84RadiiSquared = new Cartesian3(
    6378137.0 * 6378137.0,
    6378137.0 * 6378137.0,
    6356752.3142451793 * 6356752.3142451793
)
Cartesian3.magnitudeSquared = function(cartesian) {
    return (
        cartesian.x * cartesian.x +
        cartesian.y * cartesian.y +
        cartesian.z * cartesian.z
    )
}
Cartesian3.magnitude = function(cartesian) {
    return Math.sqrt(Cartesian3.magnitudeSquared(cartesian))
}

Cartesian3.normalize = function(cartesian, result) {
    const magnitude = Cartesian3.magnitude(cartesian)

    result.x = cartesian.x / magnitude
    result.y = cartesian.y / magnitude
    result.z = cartesian.z / magnitude
    return result
}

Cartesian3.multiplyComponents = function(left, right, result) {
    result.x = left.x * right.x
    result.y = left.y * right.y
    result.z = left.z * right.z
    return result
}
Cartesian3.dot = function(left, right) {
    return left.x * right.x + left.y * right.y + left.z * right.z
}
Cartesian3.add = function(left, right, result) {
    result.x = left.x + right.x
    result.y = left.y + right.y
    result.z = left.z + right.z
    return result
}
Cartesian3.divideByScalar = function(cartesian, scalar, result) {
    result.x = cartesian.x / scalar
    result.y = cartesian.y / scalar
    result.z = cartesian.z / scalar
    return result
}
Cartesian3.multiplyByScalar = function(cartesian, scalar, result) {
    result.x = cartesian.x * scalar
    result.y = cartesian.y * scalar
    result.z = cartesian.z * scalar
    return result
}
Cartesian3.fromRadians = function(
    longitude,
    latitude,
    height,
    ellipsoid,
    result
) {
    const radiiSquared = wgs84RadiiSquared
    const cosLatitude = Math.cos(latitude)
    scratchN.x = cosLatitude * Math.cos(longitude)
    scratchN.y = cosLatitude * Math.sin(longitude)
    scratchN.z = Math.sin(latitude)
    scratchN = Cartesian3.normalize(scratchN, scratchN)

    Cartesian3.multiplyComponents(radiiSquared, scratchN, scratchK)
    const gamma = Math.sqrt(Cartesian3.dot(scratchN, scratchK))
    scratchK = Cartesian3.divideByScalar(scratchK, gamma, scratchK)
    scratchN = Cartesian3.multiplyByScalar(scratchN, height, scratchN)
    result = new Cartesian3()
    return Cartesian3.add(scratchK, scratchN, result)
}

Cartesian3.fromDegrees = function(
    longitude,
    latitude,
    height,
    ellipsoid,
    result
) {
    longitude = longitude * Math.PI / 180.0
    latitude = latitude * Math.PI / 180.0
    return Cartesian3.fromRadians(longitude, latitude, height, ellipsoid, result)
}

Cartesian3.fromDegreesArrayHeights = function(coordinates, ellipsoid, result) {
    const length = coordinates.length
    if (!result) {
        result = new Array(length / 3)
    } else {
        result.length = length / 3
    }
    for (let i = 0; i < length; i += 3) {
        const longitude = coordinates[i]
        const latitude = coordinates[i + 1]
        const height = coordinates[i + 2]
        const index = i / 3
        result[index] = Cartesian3.fromDegrees(
            longitude,
            latitude,
            height,
            ellipsoid,
            result[index]
        )
    }

    return result
}
